#include "websocket_server.h"

// Define a callback to handle new connection income
void on_new_connection(WebsocketServer* s, websocketpp::connection_hdl hdl){
    std::cout << "on new connection\r\n";
    WsServer::connection_ptr con = s->m_server->get_con_from_hdl(hdl);
    auto socket = con->get_raw_socket().native_handle();
    s->m_clients[socket] = hdl;
    auto addr = con->get_socket().remote_endpoint().address();
    auto host = con->get_host();
    auto uri = con->get_uri();
    auto resource = con->get_resource();
    std::cout<<"sock" << socket << socket.remote_endpoint().port() << "addr " << addr << "host " << host << "uri" << uri
        << "resource " << resource << "\r\n";
    if (s->m_on_connection)
        s->m_on_connection(addr.to_string(),
            socket.remote_endpoint().port());

}

// Define a callback to handle incoming messages
void on_connection_close(WebsocketServer* s, websocketpp::connection_hdl hdl){
    std::cout<<"connection closed\\r\n";
    WsServer::connection_ptr con = s->m_server->get_con_from_hdl(hdl);
    auto socket = con->get_raw_socket().native_handle();
    s->m_clients.erase(socket);
	auto addr = con->get_socket().remote_endpoint().address();
	auto host = con->get_host();
	auto uri = con->get_uri();
	auto resource = con->get_resource();

    if (s->m_on_close)
        s->m_on_close(addr.to_string(), socket.remote_endpoint().port());
}

void on_connection_fail(WebsocketServer* s, websocketpp::connection_hdl hdl)
{
    std::cout << "on_connection_fail";
}

// Define a callback to handle incoming messages
void on_message(WebsocketServer* s, websocketpp::connection_hdl hdl, message_ptr msg) {
    std::cout << "on_message called with hdl: " << hdl.lock().get()
              << " and message: " << msg->get_payload()
              << std::endl;
	WsServer::connection_ptr con = s->m_server->get_con_from_hdl(hdl);
	auto socket = con->get_raw_socket().native_handle();
	s->m_clients[socket] = hdl;
	auto addr = con->get_socket().remote_endpoint().address();
	auto host = con->get_host();
	auto uri = con->get_uri();
	auto resource = con->get_resource();

    if (s->m_on_message)
        s->m_on_message(addr.to_string(),
            socket.remote_endpoint().port(), msg->get_payload());
}

std::string WebsocketServer::Url()
{
    return m_url;
}

uint32_t WebsocketServer::Port()
{
    return m_port;
}

WebsocketServer::WebsocketServer(std::string server,uint32_t port){
    m_port = port;
    m_url = server;
    m_server = new WsServer;
    m_status = START_LISTEN;

    m_thread = new std::thread([this](){
        // Set logging settings
        m_server->set_access_channels(websocketpp::log::alevel::all);
        m_server->clear_access_channels(websocketpp::log::alevel::frame_payload);
        // Initialize Asio
        m_server->init_asio();
        // Register our message handler
        m_server->set_open_handler(std::bind(&on_new_connection,this,::_1));
        m_server->set_message_handler(std::bind(&on_message,this,::_1,::_2));
        m_server->set_close_handler(std::bind(&on_connection_close,this,::_1));
        m_server->set_fail_handler(std::bind(&on_connection_fail, this, ::_1));
        websocketpp::lib::error_code ec;
        m_server->listen(websocketpp::lib::asio::ip::tcp::v4(),
            m_port, ec);
		if (ec) {
			std::cout << "listen failed because: " << ec.message() << std::endl;
            this->m_status = WebsocketServer::FAIL;
			return ;
		}
        // Start the server accept loop
        try
        {
			m_server->start_accept(ec);
			if (ec) {
				std::cout << "start_accept failed because: " << ec.message() << std::endl;
				this->m_status = WebsocketServer::FAIL;
				return;
			}
        }
        catch (const std::exception& e)
        {
            std::cout << e.what();
        }
        // Start the ASIO io_service run loop
        m_server->run();
    });
}

WebsocketServer::~WebsocketServer()
{
    /*
	this->m_status = WebsocketClient::STOP;
	if (m_tls)
		m_client_tls.stop();
	else
		m_client.stop();
	m_thread->join();
    */
}

int WebsocketServer::SendData(uint32_t fd,const char* dat,int len,
    websocketpp::frame::opcode::value op){

    auto itr = this->m_clients.find(fd);
    if(itr != m_clients.end()){
        std::error_code err;
        WsServer::connection_ptr con = m_server->get_con_from_hdl(itr->second);
        m_server->send(itr->second,dat,len,op,err);
        if(err.message() != ""){
            std::cout<<"websocket error" << err.message()<<std::endl;
            return -1;
        }
    }
    return 0;
}

int WebsocketServer::SendDataAllClient(const char* dat,int len,
    websocketpp::frame::opcode::value opcode){

    for(auto itr = m_clients.begin();itr != m_clients.end();itr++){
        std::error_code err;
        WsServer::connection_ptr con = m_server->get_con_from_hdl(itr->second);
        m_server->send(itr->second,dat,len, opcode,err);
        if(err.message() != ""){
            std::cout<<"websocket error" << err.message()<<std::endl;
            return -1;
        }
    }
    return 0;
}

void WebsocketServer::SetOnNewConnectionHanlder(OnNewConnectionHanlder handler)
{
    if (handler)
        m_on_connection = handler;
}

void WebsocketServer::SetOnConnectionCloseHanlder(OnConnectionCloseHanlder handler)
{
    if(handler)
        this->m_on_close = handler;
}

void WebsocketServer::SetOnMessageHanlder(OnMessageHanlder handler)
{
    if (handler)
        this->m_on_message = handler;
}

